use std::{
    cell::RefCell,
    clone, fs,
    future::Future,
    io::{self, Read, Write},
    pin::Pin,
    rc::Rc,
    sync::Arc,
};

use crate::tool_result::Error;
///文件是否存在
pub fn exist_dir_for_file(path: String) -> bool {
    if let Ok(metadata) = fs::metadata(path) {
        return true;
    }
    return false;
}
///是文件
pub fn is_file(path: String) -> bool {
    if let Ok(metadata) = fs::metadata(path) {
        return metadata.is_file();
    }
    return false;
}
///是文件夹
pub fn is_dir(path: String) -> bool {
    if let Ok(metadata) = fs::metadata(path) {
        return metadata.is_dir();
    }
    return false;
}
///创建文件夹
pub fn create_dir(path: String) -> Result<(), Error> {
    let re = fs::create_dir_all(path);
    if let Err(e) = re {
        return Err(Error::err(e.to_string()));
    }
    Ok(())
}
///创建文件 并返回文件
pub fn create_file_and_return(path: String) -> Result<fs::File, Error> {
    match fs::File::create(path) {
        Ok(file) => return Ok(file),
        Err(e) => return Err(Error::err(e.to_string())),
    }
}
///仅仅创建文件
pub fn create_file_only(path: String) -> Result<(), Error> {
    let re = fs::File::create(path);
    if let Err(e) = re {
        return Err(Error::err(e.to_string()));
    }
    Ok(())
}
///读取文件
pub fn read_file(path: String) -> Result<Vec<u8>, Error> {
    match fs::read(path) {
        Ok(data) => return Ok(data),
        Err(e) => return Err(Error::err(e.to_string())),
    }
}
///打开文件
pub fn open_file<T: Into<String>>(path: T) -> Result<fs::File, Error> {
    let file = fs::OpenOptions::new()
        .write(true)
        .read(true)
        .append(true)
        .open(path.into())?;
    Ok(file)
}
///删除文件夹
pub fn remove_dir_all(path: String) -> Result<(), Error> {
    fs::remove_dir_all(path)?;
    Ok(())
}
#[derive(Debug, Clone)]
pub struct FileInfo {
    pub name: String,
    pub path: String,
    pub is_dir: bool,
    pub create_time: std::time::SystemTime,
    pub modify_time: std::time::SystemTime,
    pub size: u64,
}

pub fn get_all_file_by_dir<T: Into<String>>(path: T) -> Result<Vec<FileInfo>, Error> {
    let mut file_info_list = Vec::<FileInfo>::new();
    let read_dir = fs::read_dir(path.into())?;
    for x in read_dir {
        let x = x?;
        let info = x.metadata()?;
        let file_info = FileInfo {
            name: x.file_name().to_str().unwrap().to_string(),
            path: x.path().to_str().unwrap().to_string(),
            is_dir: info.is_dir(),
            // create_time: info.created()?,
            // modify_time: info.modified()?,
            create_time: info.created().unwrap_or(std::time::SystemTime::now()),
            modify_time: info.modified().unwrap_or(std::time::SystemTime::now()),
            size: info.len(),
        };
        file_info_list.push(file_info);
    }
    Ok(file_info_list)
}

type AllFileSize = u64;
type CurFileSize = u64;
type StopZip = bool;
type CallBack = Box<
    dyn Fn(
        AllFileSize,
        CurFileSize,
        Arc<tokio::sync::RwLock<StopZip>>,
    ) -> Pin<Box<dyn Future<Output = ()>>>,
>;

pub struct ZipControl {
    pub call_back: CallBack,
}

impl ZipControl {
    pub fn new(call_back: CallBack) -> Self {
        Self { call_back }
    }
}

struct ZipControlInner {
    pub all_file_size: AllFileSize,
    pub cur_file_size: CurFileSize,
}

pub struct ZipHelper {}

impl ZipHelper {
    pub async fn zip_dir<T: Into<String>>(
        path: T,
        zip_path: T,
        zip_control: &mut ZipControl,
    ) -> Result<(), Error> {
        let path: String = path.into();
        let zip_path = zip_path.into();
        let file_info_list = get_all_file_by_dir(path.clone())?;
        if file_info_list.len() == 0 {
            return Ok(());
        }

        let mut inner = ZipControlInner {
            all_file_size: FileHelper::get_dir_size(path.clone())?,
            cur_file_size: 0,
        };

        // 输出的zip文件路径
        let zip_file_path = std::fs::File::create(zip_path)?;

        // 创建zip writer
        let mut zip = zip::ZipWriter::new(zip_file_path);

        // 设置压缩选项
        // let options =
        //     zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Deflated);
        let options = zip::write::FileOptions::default()
            .compression_method(zip::CompressionMethod::Bzip2)
            .compression_level(Some(9));
        let stop = Arc::new(tokio::sync::RwLock::new(false));
        (zip_control.call_back)(inner.all_file_size, inner.cur_file_size, stop.clone()).await;
        match Self::start_zip(
            file_info_list,
            &mut zip,
            options,
            zip_control,
            &mut inner,
            stop.clone(),
        )
        .await
        {
            Ok(_) => {}
            Err(e) => {
                // 结束写入
                match zip.finish() {
                    Ok(_) => {}
                    Err(e) => return Err(Error::err(e.to_string())),
                };
                return Err(e);
            }
        };

        // 结束写入
        match zip.finish() {
            Ok(_) => return Ok(()),
            Err(e) => return Err(Error::err(e.to_string())),
        };
    }

    async fn start_zip(
        file_info_list: Vec<FileInfo>,
        zip: &mut zip::ZipWriter<std::fs::File>,
        options: zip::write::FileOptions,
        zip_control: &mut ZipControl,
        inner: &mut ZipControlInner,
        stop: Arc<tokio::sync::RwLock<StopZip>>,
    ) -> Result<(), Error> {
        for x in file_info_list {
            let mut _stop = false;
            {
                let stop_read = stop.read().await;
                _stop = *stop_read;
            }
            if _stop {
                return Err(Error::new("备份终止"));
            }
            if x.is_dir {
                let sub_file_info_list = get_all_file_by_dir(x.path)?;
                //Self::start_zip(sub_file_info_list, zip, options, zip_control, inner,stop.clone()).await?;
                let result = Box::pin(Self::start_zip(
                    sub_file_info_list,
                    zip,
                    options,
                    zip_control,
                    inner,
                    stop.clone(),
                ));
                result.await?;
            } else {
                let mut file = match open_file(x.path.clone()) {
                    Ok(file) => file,
                    Err(e) => {
                        //println!("{}-{:?}",x.path,e);
                        return Err(e);
                    }
                };
                // 将文件名写入zip
                match zip.start_file(x.name, options) {
                    Ok(_) => {}
                    Err(e) => {
                        return Err(Error::err(e.to_string()));
                    }
                };
                let mut buffer = Vec::new();
                file.read_to_end(&mut buffer)?;
                zip.write_all(&buffer)?;
                inner.cur_file_size += x.size;
                (zip_control.call_back)(inner.all_file_size, inner.cur_file_size, stop.clone())
                    .await;
            }
        }
        Ok(())
    }
}

struct FileHelper {}

impl FileHelper {
    ///获取文件夹大小 bytes
    pub fn get_dir_size<T: Into<String>>(path: T) -> Result<u64, Error> {
        let path: String = path.into();
        if !exist_dir_for_file(path.clone()) {
            return Err(Error::new("文件夹不存在"));
        }
        let file_info_list = get_all_file_by_dir(path)?;
        let mut size: u64 = 0;
        Self::start_get_dir_size(file_info_list, &mut size)?;
        Ok(size)
    }

    fn start_get_dir_size(file_info_list: Vec<FileInfo>, size: &mut u64) -> Result<(), Error> {
        for x in file_info_list {
            if x.is_dir {
                let sub_file_info_list = get_all_file_by_dir(x.path)?;
                Self::start_get_dir_size(sub_file_info_list, size)?;
            } else {
                *size += x.size;
            }
        }
        Ok(())
    }
}
